/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Xml;
using System.Xml.XPath;
using System.Runtime.Serialization;

namespace DaveSexton.DocProject
{
	[Serializable]
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly")]
	public class XmlSettingsDictionary : BufferedSettingsDictionaryBase<string, string>
	{
		#region Public Properties
		public override bool CanCommit
		{
			get
			{
				return true;
			}
		}

		public virtual bool EnsureXmlValidNames
		{
			get
			{
				return true;
			}
		}

		public IXPathNavigable CommitNode
		{
			get
			{
				return commitNode;
			}
		}
		#endregion

		#region Private / Protected
		private readonly IXPathNavigable commitNode;
		private readonly XPathNavigator commitNavigator;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "DaveSexton.DocProject.BufferedSettingsDictionaryBase`2<System.String,System.String>.#ctor")]
		public XmlSettingsDictionary(IXPathNavigable commitNode)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		public XmlSettingsDictionary(IXPathNavigable commitNode, CultureInfo culture)
			: base(culture)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "DaveSexton.DocProject.BufferedSettingsDictionaryBase`2<System.String,System.String>.#ctor")]
		public XmlSettingsDictionary(IXPathNavigable commitNode, int capacity)
			: base(capacity)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "DaveSexton.DocProject.BufferedSettingsDictionaryBase`2<System.String,System.String>.#ctor")]
		public XmlSettingsDictionary(IXPathNavigable commitNode, IEqualityComparer<string> comparer)
			: base(comparer)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		public XmlSettingsDictionary(IXPathNavigable commitNode, CultureInfo culture, int capacity)
			: base(culture, capacity)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "DaveSexton.DocProject.BufferedSettingsDictionaryBase`2<System.String,System.String>.#ctor")]
		public XmlSettingsDictionary(IXPathNavigable commitNode, int capacity, IEqualityComparer<string> comparer)
			: base(capacity, comparer)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		public XmlSettingsDictionary(IXPathNavigable commitNode, CultureInfo culture, IEqualityComparer<string> comparer)
			: base(culture, comparer)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class.
		/// </summary>
		public XmlSettingsDictionary(IXPathNavigable commitNode, CultureInfo culture, int capacity, IEqualityComparer<string> comparer)
			: base(culture, capacity, comparer)
		{
			if (commitNode == null)
				throw new ArgumentNullException("commitNode");

			this.commitNode = commitNode;
			commitNavigator = CommitNode.CreateNavigator();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="XmlSettingsDictionary" /> class for deserialization.
		/// </summary>
		protected XmlSettingsDictionary(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}
		#endregion

		#region Methods
		public override string GetCommittedValue(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (EnsureXmlValidNames)
				name = XmlConvert.EncodeLocalName(name);

			string value = null;
			if (commitNavigator.MoveToAttribute(name, ""))
			// NOTE: GetAttribute does not return null when an attribute does not exist, so MoveToAttribute is used instead.
			{
				value = commitNavigator.Value;
				commitNavigator.MoveToParent();
			}
			return value;
		}

		public override void SetCommittedValue(string name, string value)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (EnsureXmlValidNames)
				name = XmlConvert.EncodeLocalName(name);
			
			if (commitNavigator.MoveToAttribute(name, ""))
			{
				commitNavigator.SetValue(value);
				commitNavigator.MoveToParent();
			}
			else
				commitNavigator.CreateAttribute(null, name, null, value);
		}

		public override bool IsDirty(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (!ContainsKey(name))
				return false;
			else
			{
				string attributeName = name;

				if (EnsureXmlValidNames)
					attributeName = XmlConvert.EncodeLocalName(name);

				return !string.Equals(this[name], commitNavigator.GetAttribute(attributeName, ""), StringComparison.CurrentCultureIgnoreCase);
			}
		}

		public override bool IsCommitted(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (EnsureXmlValidNames)
				name = XmlConvert.EncodeLocalName(name);

			return commitNavigator.GetAttribute(name, "") != null;
		}

		public override void Delete(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			if (EnsureXmlValidNames)
				name = XmlConvert.EncodeLocalName(name);

			if (commitNavigator.MoveToAttribute(name, ""))
				commitNavigator.DeleteSelf();
		}
		#endregion
	}
}
